package com.jneagle.xlstool.bzwgjmx.view.gui;

import com.dwarfeng.dutil.basic.cna.model.ReferenceModel;
import com.dwarfeng.dutil.basic.gui.swing.MappingTableModel;
import com.dwarfeng.dutil.basic.gui.swing.SwingUtil;
import com.dwarfeng.subgrade.stack.bean.key.LongIdKey;
import com.dwarfeng.subgrade.stack.exception.ServiceException;
import com.jneagle.xlstool.bzwgjmx.stack.bean.entity.RawErrorInfo;
import com.jneagle.xlstool.bzwgjmx.stack.bean.entity.RawPart;
import com.jneagle.xlstool.bzwgjmx.stack.bean.entity.RawPartData;
import com.jneagle.xlstool.bzwgjmx.stack.service.DataConfirmService;
import com.jneagle.xlstool.bzwgjmx.stack.service.RawErrorInfoMaintainService;
import com.jneagle.xlstool.bzwgjmx.stack.service.RawPartDataMaintainService;
import com.jneagle.xlstool.bzwgjmx.stack.service.RawPartMaintainService;
import com.jneagle.xlstool.bzwgjmx.stack.structure.ProgressObserver;
import com.jneagle.xlstool.bzwgjmx.stack.structure.ProgressStatus;
import com.jneagle.xlstool.bzwgjmx.view.handler.I18nHandler;
import com.jneagle.xlstool.bzwgjmx.view.handler.MessageHandler;
import com.jneagle.xlstool.bzwgjmx.view.handler.ModalHandler;
import com.jneagle.xlstool.bzwgjmx.view.structure.ModalItem;
import com.jneagle.xlstool.bzwgjmx.view.structure.OperationStep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.Objects;

/**
 * 数据确认面板。
 *
 * @author DwArFeng
 * @since 1.0.0
 */
@Component
@DependsOn("viewConfiguration")
public class DataConfirmPanel extends JPanel {

    private static final long serialVersionUID = -4253537822908841203L;
    private static final Logger LOGGER = LoggerFactory.getLogger(DataConfirmPanel.class);
    private static final int LAST_SELECTOR_SELECT_ALL = 0;
    private static final int LAST_SELECTOR_SELECT_CONFLICT = 1;
    private static final int LAST_SELECTOR_SELECT_NOT_APPEND = 2;

    private final ApplicationContext ctx;

    private final RawPartMaintainService rawPartMaintainService;
    private final RawPartDataMaintainService rawPartDataMaintainService;
    private final RawErrorInfoMaintainService rawErrorInfoMaintainService;
    private final DataConfirmService dataConfirmService;

    private final I18nHandler i18nHandler;
    private final ModalHandler modalHandler;
    private final ThreadPoolTaskExecutor executor;
    private final MessageHandler messageHandler;

    private final ReferenceModel<String> notificationModel;
    private final ReferenceModel<ProgressStatus> progressStatusModel;
    private final MappingTableModel<RawPart> rawPartTableModel;
    private final MappingTableModel<RawPartData> rawPartDataTableModel;
    private final MappingTableModel<RawErrorInfo> rawErrorInfoTableModel;
    private final ReferenceModel<Integer> rawDataSucceedCountModel;
    private final ReferenceModel<OperationStep> operationStepModel;

    private final ButtonGroup selectorButtonGroup = new ButtonGroup();
    private final JRadioButton selectAllButton = new JRadioButton();
    private final JRadioButton selectConflictButton = new JRadioButton();
    private final JRadioButton selectNotAppendButton = new JRadioButton();
    private final JButton selectRefreshButton = new JButton();
    private final JButton operationAppendAllButton = new JButton();
    private final JButton operationAppendNotConflictButton = new JButton();
    private final JButton operationConfirmButton = new JButton();
    private final JTable rawPartTable = new JTable();
    private final JTable rawPartDataTable = new JTable();

    private final SelectorButtonAction selectorButtonAction = new SelectorButtonAction();
    private final RawPartListSelectionListener rawPartListSelectionListener
            = new RawPartListSelectionListener();
    private final RawPartTableUpdateListener rawPartTableUpdateListener = new RawPartTableUpdateListener();
    private final DataConfirmObserver dataConfirmObserver = new DataConfirmObserver();

    private boolean rawPartSelectionAdjusting = false;

    public DataConfirmPanel(
            ApplicationContext ctx,
            RawPartMaintainService rawPartMaintainService,
            RawPartDataMaintainService rawPartDataMaintainService,
            RawErrorInfoMaintainService rawErrorInfoMaintainService,
            DataConfirmService dataConfirmService,
            I18nHandler i18nHandler,
            ModalHandler modalHandler,
            ThreadPoolTaskExecutor executor,
            MessageHandler messageHandler,
            @Qualifier("notificationModel") ReferenceModel<String> notificationModel,
            @Qualifier("progressStatusModel") ReferenceModel<ProgressStatus> progressStatusModel,
            @Qualifier("rawPartConfirmTableModel") MappingTableModel<RawPart> rawPartTableModel,
            @Qualifier("rawPartDataTableModel") MappingTableModel<RawPartData> rawPartDataTableModel,
            @Qualifier("rawErrorInfoTableModel") MappingTableModel<RawErrorInfo> rawErrorInfoTableModel,
            @Qualifier("rawDataSucceedCountModel") ReferenceModel<Integer> rawDataSucceedCountModel,
            @Qualifier("operationStepModel") ReferenceModel<OperationStep> operationStepModel
    ) {
        this.ctx = ctx;
        this.rawPartMaintainService = rawPartMaintainService;
        this.rawPartDataMaintainService = rawPartDataMaintainService;
        this.rawErrorInfoMaintainService = rawErrorInfoMaintainService;
        this.dataConfirmService = dataConfirmService;
        this.i18nHandler = i18nHandler;
        this.modalHandler = modalHandler;
        this.executor = executor;
        this.messageHandler = messageHandler;
        this.notificationModel = notificationModel;
        this.progressStatusModel = progressStatusModel;
        this.rawPartTableModel = rawPartTableModel;
        this.rawPartDataTableModel = rawPartDataTableModel;
        this.rawErrorInfoTableModel = rawErrorInfoTableModel;
        this.rawDataSucceedCountModel = rawDataSucceedCountModel;
        this.operationStepModel = operationStepModel;
    }

    @SuppressWarnings("DuplicatedCode")
    @PostConstruct
    public void postConstruct() {
        // 初始化界面。
        GridBagLayout gridBagLayout = new GridBagLayout();
        gridBagLayout.columnWidths = new int[]{0, 0};
        gridBagLayout.rowHeights = new int[]{0, 0, 0};
        gridBagLayout.columnWeights = new double[]{0.0, 1.0};
        gridBagLayout.rowWeights = new double[]{0.0, 0.0, 1.0};
        setLayout(gridBagLayout);

        JLabel selectorLabel = new JLabel(i18nHandler.getMessage("ui.label.044"));
        GridBagConstraints gbcSelectorLabel = new GridBagConstraints();
        gbcSelectorLabel.insets = new Insets(0, 0, 0, 5);
        gbcSelectorLabel.anchor = GridBagConstraints.EAST;
        gbcSelectorLabel.gridx = 0;
        gbcSelectorLabel.gridy = 0;
        add(selectorLabel, gbcSelectorLabel);

        JPanel selectorPanel = new JPanel();
        FlowLayout selectorPanelLayout = new FlowLayout();
        selectorPanelLayout.setAlignment(FlowLayout.LEADING);
        selectorPanelLayout.setVgap(0);
        selectorPanel.setLayout(selectorPanelLayout);
        GridBagConstraints gbcSelectorPanel = new GridBagConstraints();
        gbcSelectorPanel.insets = new Insets(0, 0, 0, 5);
        gbcSelectorPanel.anchor = GridBagConstraints.CENTER;
        gbcSelectorPanel.fill = GridBagConstraints.BOTH;
        gbcSelectorPanel.gridx = 1;
        gbcSelectorPanel.gridy = 0;
        add(selectorPanel, gbcSelectorPanel);

        selectAllButton.setAction(selectorButtonAction);
        selectAllButton.setText(i18nHandler.getMessage("ui.label.048"));
        selectorButtonGroup.add(selectAllButton);
        selectorPanel.add(selectAllButton);

        selectConflictButton.setAction(selectorButtonAction);
        selectConflictButton.setText(i18nHandler.getMessage("ui.label.049"));
        selectorButtonGroup.add(selectConflictButton);
        selectorPanel.add(selectConflictButton);

        selectNotAppendButton.setAction(selectorButtonAction);
        selectNotAppendButton.setText(i18nHandler.getMessage("ui.label.050"));
        selectorButtonGroup.add(selectNotAppendButton);
        selectorPanel.add(selectNotAppendButton);

        selectRefreshButton.setAction(new SelectRefreshButtonAction());
        selectRefreshButton.setText(i18nHandler.getMessage("ui.label.053"));
        selectorPanel.add(selectRefreshButton);

        JLabel operationLabel = new JLabel(i18nHandler.getMessage("ui.label.045"));
        GridBagConstraints gbcResultLabel = new GridBagConstraints();
        gbcResultLabel.insets = new Insets(0, 0, 0, 5);
        gbcResultLabel.anchor = GridBagConstraints.EAST;
        gbcResultLabel.gridx = 0;
        gbcResultLabel.gridy = 1;
        add(operationLabel, gbcResultLabel);

        JPanel operationPanel = new JPanel();
        FlowLayout operationPanelLayout = new FlowLayout();
        operationPanelLayout.setAlignment(FlowLayout.LEADING);
        operationPanelLayout.setVgap(0);
        operationPanel.setLayout(operationPanelLayout);
        GridBagConstraints gbcOperationPanel = new GridBagConstraints();
        gbcOperationPanel.insets = new Insets(0, 0, 0, 5);
        gbcOperationPanel.anchor = GridBagConstraints.CENTER;
        gbcOperationPanel.fill = GridBagConstraints.BOTH;
        gbcOperationPanel.gridx = 1;
        gbcOperationPanel.gridy = 1;
        add(operationPanel, gbcOperationPanel);

        operationAppendAllButton.setAction(new OperationAppendAllAction());
        operationAppendAllButton.setText(i18nHandler.getMessage("ui.label.051"));
        operationPanel.add(operationAppendAllButton);

        operationAppendNotConflictButton.setAction(new OperationAppendNotConflictAction());
        operationAppendNotConflictButton.setText(i18nHandler.getMessage("ui.label.052"));
        operationPanel.add(operationAppendNotConflictButton);

        operationConfirmButton.setAction(new ConfirmAction());
        operationConfirmButton.setText(i18nHandler.getMessage("ui.label.056"));
        operationPanel.add(operationConfirmButton);

        JPanel bodyPanel = new JPanel();
        GridBagLayout bodyPanelGridBagLayout = new GridBagLayout();
        bodyPanelGridBagLayout.columnWidths = new int[]{0, 0};
        bodyPanelGridBagLayout.rowHeights = new int[]{0, 0};
        bodyPanelGridBagLayout.columnWeights = new double[]{2.0, 1.0};
        bodyPanelGridBagLayout.rowWeights = new double[]{0, 1.0};
        bodyPanel.setLayout(bodyPanelGridBagLayout);
        GridBagConstraints gbcBodyPanel = new GridBagConstraints();
        gbcBodyPanel.insets = new Insets(5, 0, 0, 0);
        gbcBodyPanel.fill = GridBagConstraints.BOTH;
        gbcBodyPanel.gridwidth = 2;
        gbcBodyPanel.gridx = 0;
        gbcBodyPanel.gridy = 2;
        add(bodyPanel, gbcBodyPanel);

        JLabel rawPartLabel = new JLabel(i18nHandler.getMessage("ui.label.067"));
        GridBagConstraints gbcRawPartLabel = new GridBagConstraints();
        gbcRawPartLabel.insets = new Insets(0, 5, 0, 0);
        gbcRawPartLabel.fill = GridBagConstraints.BOTH;
        gbcRawPartLabel.gridx = 0;
        gbcRawPartLabel.gridy = 0;
        bodyPanel.add(rawPartLabel, gbcRawPartLabel);

        JLabel rawPartDataLabel = new JLabel(i18nHandler.getMessage("ui.label.068"));
        GridBagConstraints gbcRawPartDataLabel = new GridBagConstraints();
        gbcRawPartDataLabel.insets = new Insets(0, 5, 0, 0);
        gbcRawPartDataLabel.fill = GridBagConstraints.BOTH;
        gbcRawPartDataLabel.gridx = 1;
        gbcRawPartDataLabel.gridy = 0;
        bodyPanel.add(rawPartDataLabel, gbcRawPartDataLabel);

        I18nTableCellRenderer rawPartHeaderRender = ctx.getBean(
                I18nTableCellRenderer.class, ctx.getBean(I18nHandler.class),
                rawPartTable.getTableHeader().getDefaultRenderer()
        );
        rawPartTable.getTableHeader().setDefaultRenderer(rawPartHeaderRender);
        rawPartTable.setModel(rawPartTableModel);
        rawPartTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        // 为表格设置渲染器和编辑器。
        for (int i = 0; i < rawPartTable.getColumnModel().getColumnCount(); i++) {
            TableColumn column = rawPartTable.getColumnModel().getColumn(i);
            // 前三行设置颜色渲染器，如果冲突则文本颜色显示红色。
            if (i < 4) {
                column.setCellRenderer(new TextColorCellRenderer());
            }
            // 最后一行设置布尔CheckBox渲染器和编辑器。
            if (i == 4) {
                column.setCellRenderer(new BooleanCheckBoxCellRenderer());
                column.setCellEditor(new BooleanCellEditor());
            }
        }
        JScrollPane rawPartScrollPane = new JScrollPane();
        rawPartScrollPane.setViewportView(rawPartTable);
        GridBagConstraints gbcRawPartScrollPane = new GridBagConstraints();
        gbcRawPartScrollPane.insets = new Insets(0, 0, 0, 0);
        gbcRawPartScrollPane.fill = GridBagConstraints.BOTH;
        gbcRawPartScrollPane.gridx = 0;
        gbcRawPartScrollPane.gridy = 1;
        bodyPanel.add(rawPartScrollPane, gbcRawPartScrollPane);

        I18nTableCellRenderer rawPartDataHeaderRender = ctx.getBean(
                I18nTableCellRenderer.class, ctx.getBean(I18nHandler.class),
                rawPartDataTable.getTableHeader().getDefaultRenderer()
        );
        rawPartDataTable.getTableHeader().setDefaultRenderer(rawPartDataHeaderRender);
        rawPartDataTable.setModel(rawPartDataTableModel);
        JScrollPane rawPartDataScrollPane = new JScrollPane();
        rawPartDataScrollPane.setViewportView(rawPartDataTable);
        GridBagConstraints gbcRawPartDataScrollPane = new GridBagConstraints();
        gbcRawPartDataScrollPane.insets = new Insets(0, 0, 0, 0);
        gbcRawPartDataScrollPane.fill = GridBagConstraints.BOTH;
        gbcRawPartDataScrollPane.gridx = 1;
        gbcRawPartDataScrollPane.gridy = 1;
        bodyPanel.add(rawPartDataScrollPane, gbcRawPartDataScrollPane);

        // 添加侦听器。
        rawPartTable.getSelectionModel().addListSelectionListener(rawPartListSelectionListener);
        rawPartTableModel.addTableModelListener(rawPartTableUpdateListener);

        // 同步属性。
        syncProperties();
    }

    private void syncProperties() {
        Integer lastSelector = modalHandler.getParsedValidValue(
                ModalItem.MODAL_DATA_CONFIRM_LAST_SELECTOR, Integer.class
        );
        SwingUtil.invokeInEventQueue(() -> {
            switch (lastSelector) {
                case LAST_SELECTOR_SELECT_ALL:
                    selectAllButton.setSelected(true);
                    selectConflictButton.setSelected(false);
                    selectNotAppendButton.setSelected(false);
                    break;
                case LAST_SELECTOR_SELECT_CONFLICT:
                    selectAllButton.setSelected(false);
                    selectConflictButton.setSelected(true);
                    selectNotAppendButton.setSelected(false);
                    break;
                case LAST_SELECTOR_SELECT_NOT_APPEND:
                    selectAllButton.setSelected(false);
                    selectConflictButton.setSelected(false);
                    selectNotAppendButton.setSelected(true);
                    break;
                default:
                    selectAllButton.setSelected(false);
                    selectConflictButton.setSelected(false);
                    selectNotAppendButton.setSelected(false);
            }
        });
    }

    @PreDestroy
    public void preDestroy() {
        // 移除侦听器。
        rawPartTable.getSelectionModel().removeListSelectionListener(rawPartListSelectionListener);
        rawPartTableModel.removeTableModelListener(rawPartTableUpdateListener);
    }

    public void loadData() {
        // 清除表格的选择。
        rawPartSelectionAdjusting = true;
        rawPartTableModel.clear();
        rawPartDataTableModel.clear();
        rawErrorInfoTableModel.clear();
        executor.submit(() -> {
            try {
                List<RawPart> rawPartList;
                if (selectAllButton.isSelected()) {
                    rawPartList = rawPartMaintainService.lookupAsList();
                } else if (selectConflictButton.isSelected()) {
                    rawPartList = rawPartMaintainService.lookupAsList(RawPartMaintainService.CONFLICT, new Object[0]);
                } else if (selectNotAppendButton.isSelected()) {
                    rawPartList = rawPartMaintainService.lookupAsList(RawPartMaintainService.NOT_APPEND, new Object[0]);
                } else {
                    rawPartList = rawPartMaintainService.lookupAsList();
                }
                rawDataSucceedCountModel.set(rawPartDataMaintainService.lookupAsList().size());
                List<RawErrorInfo> rawErrorInfoList = rawErrorInfoMaintainService.lookupAsList();
                rawPartTableModel.addAll(rawPartList);
                rawErrorInfoTableModel.addAll(rawErrorInfoList);
            } catch (Exception e) {
                messageHandler.error(this, "ui.label.031");
            } finally {
                rawPartSelectionAdjusting = false;
            }
        });
    }

    @SuppressWarnings("DuplicatedCode")
    public void loadChildData() {
        rawPartDataTableModel.clear();
        LongIdKey rawPartKey = rawPartTableModel.get(rawPartTable.getSelectionModel().getAnchorSelectionIndex()).getKey();
        executor.submit(() -> {
            try {
                List<RawPartData> rawPartDataList = rawPartDataMaintainService.lookupAsList(
                        RawPartDataMaintainService.CHILD_FOR_RAW_PART, new Object[]{rawPartKey}
                );
                rawPartDataTableModel.addAll(rawPartDataList);
            } catch (Exception e) {
                messageHandler.error(this, "ui.label.031");
            }
        });
    }

    private class SelectorButtonAction extends AbstractAction {

        private static final long serialVersionUID = 8191110661486899582L;

        @Override
        public void actionPerformed(ActionEvent e) {
            // 根据 RadioButton 选择的不同，向模态处理器中记录不同的数据。
            if (selectAllButton.isSelected()) {
                modalHandler.setParsedValue(ModalItem.MODAL_DATA_CONFIRM_LAST_SELECTOR, LAST_SELECTOR_SELECT_ALL);
            } else if (selectConflictButton.isSelected()) {
                modalHandler.setParsedValue(ModalItem.MODAL_DATA_CONFIRM_LAST_SELECTOR, LAST_SELECTOR_SELECT_CONFLICT);
            } else if (selectNotAppendButton.isSelected()) {
                modalHandler.setParsedValue(ModalItem.MODAL_DATA_CONFIRM_LAST_SELECTOR, LAST_SELECTOR_SELECT_NOT_APPEND);
            }
            // 调用查询方法。
            loadData();
        }
    }

    private class RawPartListSelectionListener implements ListSelectionListener {

        @Override
        public void valueChanged(ListSelectionEvent e) {
            if (e.getValueIsAdjusting() || rawPartSelectionAdjusting) {
                return;
            }
            loadChildData();
        }
    }

    private class OperationAppendAllAction extends AbstractAction implements Runnable {

        private static final long serialVersionUID = -1154623895201339919L;

        @Override
        public void actionPerformed(ActionEvent e) {
            executor.submit(this);
        }

        @Override
        public void run() {
            try {
                List<RawPart> rawParts = rawPartMaintainService.lookupAsList();
                rawParts.forEach(e -> e.setAppendFlag(true));
                rawPartMaintainService.batchUpdate(rawParts);
                loadData();
            } catch (Exception e) {
                messageHandler.error(DataConfirmPanel.this, "ui.label.015");
                LOGGER.warn("无法更新原始部件，异常信息如下: ", e);
            }
        }
    }

    private class OperationAppendNotConflictAction extends AbstractAction implements Runnable {

        private static final long serialVersionUID = -6487966665403722372L;

        @Override
        public void actionPerformed(ActionEvent e) {
            executor.submit(this);
        }

        @Override
        public void run() {
            try {
                List<RawPart> rawParts = rawPartMaintainService.lookupAsList();
                rawParts.forEach(e -> e.setAppendFlag(!e.isConflictFlag()));
                rawPartMaintainService.batchUpdate(rawParts);
                loadData();
            } catch (Exception e) {
                messageHandler.error(DataConfirmPanel.this, "ui.label.015");
                LOGGER.warn("无法更新原始部件，异常信息如下: ", e);
            }
        }
    }

    private class SelectRefreshButtonAction extends AbstractAction {

        private static final long serialVersionUID = -4328741872129899409L;

        @Override
        public void actionPerformed(ActionEvent e) {
            loadData();
        }
    }

    private class TextColorCellRenderer extends DefaultTableCellRenderer {

        private static final long serialVersionUID = 8662080111995021164L;

        @Override
        public java.awt.Component getTableCellRendererComponent(
                JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column
        ) {
            // 获取父类渲染得到的标签。
            JLabel label = (JLabel) super.getTableCellRendererComponent(
                    table, value, isSelected, hasFocus, row, column
            );

            // 如果数据是冲突的，则文本颜色为红色，否则是黑色。
            if (rawPartTableModel.get(row).isConflictFlag()) {
                label.setForeground(Color.RED);
            } else {
                label.setForeground(Color.BLACK);
            }

            if (value instanceof Boolean) {
                label.setText(
                        (boolean) value ?
                                i18nHandler.getMessage("ui.label.054") :
                                i18nHandler.getMessage("ui.label.055")
                );
            }

            // 返回标签。
            return label;
        }
    }

    private static class BooleanCheckBoxCellRenderer extends CentralCheckBox implements TableCellRenderer {

        private static final long serialVersionUID = 4930102434960135590L;

        public BooleanCheckBoxCellRenderer() {
            setOpaque(true);
            setHorizontalAlignment(SwingConstants.CENTER);
        }

        @Override
        public java.awt.Component getTableCellRendererComponent(
                JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column
        ) {
            if (isSelected) {
                setForeground(table.getSelectionForeground());
                setBackground(table.getSelectionBackground());
            } else {
                Color background = table.getBackground();
                if (Objects.nonNull(background)) {
                    background = new Color(
                            background.getRed(), background.getGreen(), background.getBlue(), background.getAlpha()
                    );
                }
                Color alternateColor = UIManager.getColor("Table.alternateRowColor");
                if (alternateColor != null && row % 2 != 0) {
                    background = alternateColor;
                    background = new Color(
                            background.getRed(), background.getGreen(), background.getBlue(), background.getAlpha()
                    );
                }
                setBackground(background);
            }

            setFont(table.getFont());

            if (hasFocus) {
                Border border = null;
                if (isSelected) {
                    border = UIManager.getBorder("Table.focusSelectedCellHighlightBorder");
                }
                if (Objects.isNull(border)) {
                    border = UIManager.getBorder("Table.focusCellHighlightBorder");
                }
                setBorder(border);

                if (!isSelected && table.isCellEditable(row, column)) {
                    Color col;
                    col = UIManager.getColor("Table.focusCellForeground");
                    if (col != null) {
                        super.setForeground(col);
                    }
                    col = UIManager.getColor("Table.focusCellBackground");
                    if (col != null) {
                        super.setBackground(col);
                    }
                }
            } else {
                setBorder(new EmptyBorder(1, 1, 1, 1));
            }

            setSelected((boolean) value);

            return this;
        }

        /**
         * 减少非必要的行为以提高显示性能。
         */
        @Override
        public void invalidate() {
        }

        /**
         * 减少非必要的行为以提高显示性能。
         */
        @Override
        public void validate() {
        }

        /**
         * 减少非必要的行为以提高显示性能。
         */
        @Override
        public void revalidate() {
        }

        /**
         * 减少非必要的行为以提高显示性能。
         */
        @Override
        public void repaint(long tm, int x, int y, int width, int height) {
        }

        /**
         * 减少非必要的行为以提高显示性能。
         */
        @Override
        public void repaint(Rectangle r) {
        }

        /**
         * 减少非必要的行为以提高显示性能。
         */
        @Override
        public void repaint() {
        }
    }

    private static class BooleanCellEditor extends DefaultCellEditor implements TableCellEditor {

        private static final long serialVersionUID = 4432283739166212183L;

        public BooleanCellEditor() {
            super(new CentralCheckBox());
        }
    }

    private static class CentralCheckBox extends JCheckBox {

        private static final long serialVersionUID = -4198773659150233279L;

        public CentralCheckBox() {
            setOpaque(true);
            setHorizontalAlignment(SwingConstants.CENTER);
        }
    }

    private class RawPartTableUpdateListener implements TableModelListener {

        @Override
        public void tableChanged(TableModelEvent e) {
            if (!Objects.equals(e.getType(), TableModelEvent.UPDATE)) {
                return;
            }
            executor.submit(new RawPartUpdateTask(e.getFirstRow(), e.getLastRow()));
        }
    }

    private class RawPartUpdateTask implements Runnable {

        private final int firstRow;
        private final int lastRow;

        public RawPartUpdateTask(int firstRow, int lastRow) {
            this.firstRow = firstRow;
            this.lastRow = lastRow;
        }

        @Override
        public void run() {
            try {
                List<RawPart> rawParts = rawPartTableModel.subList(firstRow, lastRow + 1);
                rawPartMaintainService.batchUpdate(rawParts);
            } catch (Exception e) {
                messageHandler.error(DataConfirmPanel.this, "ui.label.015");
                LOGGER.warn("无法更新原始部件，异常信息如下: ", e);
            }
        }
    }

    private class ConfirmAction extends AbstractAction implements Runnable {

        private static final long serialVersionUID = -7030524062631603956L;

        @Override
        public void actionPerformed(ActionEvent e) {
            // 在后台线程中执行方法，避免前端界面卡顿。
            executor.execute(this);
        }

        @Override
        public void run() {
            try {
                // 更新通知面板标签文本。
                notificationModel.set(i18nHandler.getMessage("ui.label.057"));

                // 创建侦听
                dataConfirmService.addObserver(dataConfirmObserver);

                // 调用接口确认数据，并捕获异常。
                try {
                    dataConfirmService.confirm();
                } catch (ServiceException e) {
                    messageHandler.warn(DataConfirmPanel.this, "ui.label.015");
                    // 更新通知面板标签文本。
                    notificationModel.set(i18nHandler.getMessage("ui.label.058"));
                    return;
                } finally {
                    // 移除侦听。
                    dataConfirmService.removeObserver(dataConfirmObserver);
                }

                // 更新前端界面的操作步骤。
                operationStepModel.set(OperationStep.DATA_EXPORT);
            } catch (Exception e) {
                messageHandler.error(DataConfirmPanel.this, "ui.label.015");
                LOGGER.warn("无法处理数据，异常信息如下: ", e);
                operationStepModel.set(OperationStep.DATA_CONFIRM);
            }
        }
    }

    private class DataConfirmObserver implements ProgressObserver {

        @Override
        public void onProgressChanged(ProgressStatus progressStatus) {
            progressStatusModel.set(progressStatus);
        }
    }
}
